<?php

declare(strict_types=1);

namespace App\Services\Message;

use App\Constants\Message\SmsCode;
use App\Exception\InternalException;
use App\Exception\InvalidRequestException;
use App\Utils\Other\Message\App;
use App\Utils\Services\BaseServices;
use App\Model\Message\Sms as SmsModel;
use function Hyperf\Translation\__;
use function Hyperf\Support\make;

Class Sms extends BaseServices
{
    public function model():string
    {
        return SmsModel::class;
    }

    /**
     * 发送验证码
     * @param string $account
     * @param string $sign
     * @param int $scene
     * @param int $num
     * @return \App\Model\Model|\Hyperf\Database\Model\Model|mixed
     */
    public function code(string $account, string $sign=SmsCode::SIGN_KJSB, int $scene=SmsCode::SCENE_MOBILE_VERIFICATION, int $num=5)
    {
        $count = $this->where([
            ['account', '=', $account],
            ['sign', '=', $sign],
            ['scene', '=', $scene]
        ])->whereDay('send_at', new \DateTime())
            ->where('status', 1)
            ->where('scene', $scene)
            ->count('id');
        if ($count > $num) {
            throw new InvalidRequestException(__('message.sms.num', compact('num')));
        }
        $chick = 1;
        $exists = $this->where([
            ['account', '=', $account],
            ['sms_type', '=', SmsCode::SMS_TYPE_CODE],
            ['created_at', '>', date('Y-m-d H:i:s', time()-$chick * 60)],
            ['scene', '=', $scene]
        ])->exists();
        if ($exists) {
            throw new InvalidRequestException(__('message.sms.chick', compact('chick')));
        }

        $code = $this->getCode();
        $msg = "【{$sign}】".sprintf(SmsCode::$sceneMsgMap[$scene], $code);
        $model = $this->create([
            'account' => $account,
            'message' => $msg,
            'sign' => $sign,
            'code' => $code
        ]);
        if (!$model->exists) {
            throw new InternalException();
        }

        $sms = make(App::class);
        try {
            $sms->handle($account, $msg, $model->type);
        } catch (InternalException $exception) {
            $model->status = 2;
            $model->error_msg = $exception->getMessage();
            $model->save();
            throw $exception;
        }
        $model->status = 1;
        $model->send_at = date('Y-m-d H:i:s');
        $model->save();

        return $model;
    }

    /**
     * 获取验证码
     * @param int $len
     * @return int
     */
    public function getCode(int $len=6)
    {
        $min = intval(str_pad('1', $len, '0'));
        $max = intval(str_pad('9', $len, '9'));

        $code = mt_rand($min, $max);
        $exists = $this->where('code', $code)
            ->whereTime('created_at', '>', time()-5*60)
            ->where('type', SmsCode::SMS_TYPE_CODE)
            ->exists();
        if ($exists) {
            return $this->getCode($len);
        }

        return $code;
    }

    /**
     * 验证二维码
     * @param string $account
     * @param int|string $code
     * @param int $scene
     * @return bool
     */
    public function verification(string $account, int|string $code, int $scene=SmsCode::SCENE_MOBILE_VERIFICATION):bool
    {
        $model = $this->where([
            ['account', '=', $account],
            ['code', '=', $code],
            ['scene', '=', $scene],
            ['status', '=', 1],
            ['sms_type', '=', SmsCode::SMS_TYPE_CODE],
            ['send_at', '>=', date('Y-m-d H:i:s', time()-5*60)],
            ['is_verfication', '=', 0]
        ])->first();
        if (!$model) {
            throw new InvalidRequestException(__('message.sms.code.failed'));
        }

        return $model->fill(['is_verfication'=>1])->save();
    }
}
